package jmine.tec.services.environment;

import java.io.InputStream;
import java.util.List;

import jmine.tec.environment.db.DBEnvironment;
import jmine.tec.executionlog.dao.ExecutionLogDAO;
import jmine.tec.executionlog.dao.ExecutionLogItemDAO;
import jmine.tec.executionlog.domain.ExecutionLog;
import jmine.tec.executionlog.domain.ExecutionLogItem;
import jmine.tec.executionlog.domain.enumx.ExecutionStatus;
import jmine.tec.services.ServicesController;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.transaction.TransactionStatus;

import bancosys.tec.datadigester.DataDigesterController;
import bancosys.tec.persist.dataset.HibernateRandomAccessResultSet;
import bancosys.tec.utils.db.descriptor.FlatReferenceDatabaseBuilder.ReferenceDatabaseDescriptionType;

/**
 * Implementação de um ambiente de banco de dados com base de referência.
 * 
 * @author Cesar Olmos
 */
public class RefDBEnvironment extends DBEnvironment {

    private static final Logger LOGGER = Logger.getLogger(RefDBEnvironment.class);

    static {
        DBEnvironment.register(new ServicePostscriptEntryLoader());
        DBEnvironment.register(new DigesterPostscriptEntryLoader());
    }
    
    /**
     * Construtor de cópia.
     * 
     * @param other o outro objeto a ser copiado.
     */
    public RefDBEnvironment(DBEnvironment other) {
        super(other);
    }

    /**
     * Construtor.
     * 
     * @param bf a beanFactory.
     * @param environmentKey o arquivo de configuração do spring.
     * @param resourcesBaseReferencia os recursos da base de referência.
     * @param databaseServerType o tipo do servidor de base de dados.
     * @param refdbType o tipo da base de referência.
     */
    public RefDBEnvironment(BeanFactory bf, String environmentKey, String databaseServerType, String[] resourcesBaseReferencia,
            ReferenceDatabaseDescriptionType refdbType) {
        super(bf, environmentKey, databaseServerType, resourcesBaseReferencia, refdbType);
    }

    /**
     * Delega ao DataDigesterController a execução de um arquivo por um processor pré configurado.
     * 
     * @param processorName nome do processor.
     * @param fileName nome do arquivo.
     * @param isRollbackonly se o método deve dar rollback após execução. Indicado para planilhas de teste.
     */
    public void executaTransacional(String processorName, String fileName, boolean isRollbackonly) {
        InputStream iostream = this.getClass().getClassLoader().getResourceAsStream(fileName);
        if (iostream == null) {
            throw new RuntimeException("Arquivo " + fileName + " não pode ser carregado.");
        }

        TransactionStatus ts = this.getHelper().getTransactionStatus();
        try {
            this.getDataDigesterController().fastProcess(processorName, fileName, iostream);
            if (!isRollbackonly) {
                this.getHelper().commit(ts);
            }
        } catch (Throwable t) {
            LOGGER.error(t.getMessage(), t);
            throw new RuntimeException(this.showErrors());
        } finally {
            if (isRollbackonly) {
                this.getHelper().rollback(ts);
            }
        }
    }

    /**
     * DataDigesterController
     * 
     * @return DataDigesterController
     */
    private DataDigesterController getDataDigesterController() {
        return (DataDigesterController) this.getHelper().getBeanFactory().getBean("dataDigesterController", DataDigesterController.class);
    }

    /**
     * Return the ServicesController
     * 
     * @return the ServicesController
     */
    private ServicesController getServicesController() {
        return (ServicesController) this.getHelper().getBeanFactory().getBean("servicesController", ServicesController.class);
    }

    /**
     * Retorna os erros registrados no execution log.
     * 
     * @return erros do execution log.
     */
    private String showErrors() {
        ExecutionLogDAO logDao = this.getServicesController().getDAOFactory().getDAOByClass(ExecutionLogDAO.class);
        List<ExecutionLog> logs = logDao.findAll();
        StringBuilder sb = new StringBuilder();
        ExecutionLogItemDAO logItemDAO = this.getServicesController().getDAOFactory().getDAOByClass(ExecutionLogItemDAO.class);

        for (ExecutionLog executionLog : logs) {
            if (executionLog.getStatus().equals(ExecutionStatus.NOT_OK)) {
                HibernateRandomAccessResultSet<ExecutionLogItem> set = logItemDAO.findByExecutionLog(executionLog, null);
                for (ExecutionLogItem item : set) {
                    sb.append("Erro na execução do serviço: \n");
                    sb.append(item.getContent());
                    sb.append("\n");
                    sb.append(item.getStackTrace());
                    sb.append("\n");
                    sb.append(item.getMessage());
                }
            }
        }
        return sb.toString();
    }
}
